package com.kuizii.demo.core.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kuizii.base.common.utils.BeanHelper;
import com.kuizii.base.domain.BaseResponse;
import com.kuizii.demo.api.UserService;
import com.kuizii.demo.core.data.service.SysUserDataService;
import com.kuizii.demo.core.domain.entity.user.SysUserEntity;
import com.kuizii.demo.domain.SysUserDto;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.shaded.com.google.common.hash.BloomFilter;
import org.apache.curator.shaded.com.google.common.hash.Funnels;
import org.apache.dubbo.apidocs.annotations.ApiDoc;
import org.apache.dubbo.apidocs.annotations.ApiModule;
import org.apache.dubbo.config.annotation.DubboService;
import org.redisson.RedissonFairLock;
import org.redisson.RedissonLock;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;




import javax.annotation.PostConstruct;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.Objects;
import java.util.Optional;

/**
 * <p>
 * 如果业务微复杂 ,由Service层聚合dataService层以及manager层的数据,提供服务
 * <p/>
 *
 * @author nuanyang
 * Email freexiaodong@163.com
 * Created by 2019-06-16 16:32:52
 */

@Api(value = "用户表接口文档")
@ApiModule(value = "测试接口服务", apiInterface = UserService.class)
@DubboService(version = "1.0", group = "demo")
@Slf4j
public class UserServiceImpl implements UserService {

    BloomFilter<String> bloomFilter;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    SysUserDataService sysUserDataService;


    @PostConstruct
    public void init() {
        //将数据库中存在的ID放入布隆过滤器中，后续不在过滤器中的将不会查询数据库，避免击穿
        bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), 1L, 0.1);
        LambdaQueryWrapper<SysUserEntity> query = new QueryWrapper<SysUserEntity>().lambda();
        query.select(SysUserEntity::getId);

//        kafkaTemplate.send("info","fadfadfadf");
//        sysUserDataService.listObjs(query).forEach(x -> bloomFilter.put(x.toString()));
    }


    //调用manager层
    //@Autowired
    //XXManager xxManager;

    @ApiDoc(value = "测试查询接口", description = "通过id查用户", responseClassDescription = "返回用户DTO")
    @Override
    public SysUserDto getUserById(String userId) {


        SysUserDto result = null;
        RReadWriteLock lock = redissonClient.getReadWriteLock(userId);
        RLock readLock = lock.readLock();
        try {
            readLock.lock();
            if (!bloomFilter.mightContain(userId)) {
                //不在布隆过滤器，说明对象一定不存在! 而在过滤器，由于hash碰撞是有概率 明明不存在却为true
                return null;
            }
            SysUserEntity entity = sysUserDataService.getById(userId);
            result = BeanHelper.copyProperties(entity, SysUserDto::new);

        } finally {
            readLock.unlock();
        }
        return result;
    }

    @ApiDoc(value = "测试保存接口", description = "保存新用户", responseClassDescription = "无需返回")
    @Override
    public void addUser(@Valid SysUserDto dto) {
        SysUserEntity result = BeanHelper.copyProperties(dto, SysUserEntity::new);
        boolean res = sysUserDataService.saveOrUpdate(result);
        if (res) {
            //添加到布隆过滤器
            bloomFilter.put(dto.getId());
        }

    }

    /**
     * 演示读写锁,mybatis 本身也支持乐观锁，这里仅用来演示分布式锁场景
     *
     * @param
     */
    @ApiDoc(value = "测试更新")
    @Override
//    @Transactional(rollbackFor = Exception.class)  //不要开启事务，如开事务，锁不能在方法中，就必须在该方法前加锁，方法执行后解锁
    public String addScore(String userId, BigDecimal score) throws Exception {

        if (StringUtils.isBlank(userId) || !bloomFilter.mightContain(userId)) {
            log.debug("查询对象或ID为空值 ");
            return "查询对象或ID为空值";
        }
        RReadWriteLock rReadWriteLock = redissonClient.getReadWriteLock(userId);

        RLock writeLock = rReadWriteLock.writeLock();
        try {
            writeLock.lock();


            /**
             todo something
             比如异步推送MQ，发短信，邮件等其它业务
             */

            SysUserEntity userEntity = sysUserDataService.getById(userId);
            if (userEntity.getScore().add(score).compareTo(BigDecimal.ZERO) < 0) {
                throw new RuntimeException("出错，积分不足！");
            }
            log.debug(userEntity.getScore().toString());
            userEntity.setScore(userEntity.getScore().add(score));
            boolean optRst = sysUserDataService.updateById(userEntity);
            if (!optRst) {
                throw new RuntimeException("更新失败");
            }
            return BaseResponse.ok().getMessage();

            //模拟缓慢或阻塞
//            Thread.sleep(1000);
        } finally {
            writeLock.unlock();
        }
    }


}
